package com.koi.common.utils.decimal;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * @author ･ᴗ･
 * @description [ArithmeticUtils-用于高精确处理常用的数学运算]
 */
public class BigDecimalUtil {

    // 默认除法运算精度
    private static final int DEF_DIV_SCALE = 10;

    /**
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     * @description [提供精确的加法运算]
     */

    public static double add(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }

    /**
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     * @description [提供精确的加法运算]
     */
    public static BigDecimal add(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2);
    }

    /**
     * @param v1    被减数
     * @param v2    减数
     * @param scale 保留scale 位小数
     * @param v1    被加数
     * @param v2    加数
     * @param scale 保留scale 位小数
     * @return 两个参数的和
     * @description [提供精确的加法运算]
     * BigDecimal.ROUND_UP 进位处理：例如：1.654 会变成 1.66
     * BigDecimal.ROUND_DOWN 直接删除多余的小数位：例如2.35 会变成 2.3
     * BigDecimal.ROUND_HALF_UP 四舍五入
     */
    public static String add(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2).setScale(scale, RoundingMode.HALF_UP).toString();
    }

    /**
     * @param v1    被减数
     * @param v2    减数
     * @param scale 保留scale 位小数
     * @param v1    被加数
     * @param v2    加数
     * @param scale 保留scale 位小数
     * @return 两个参数的和
     * @description [提供精确的加法运算，进位处理]
     * BigDecimal.ROUND_UP 进位处理：例如：1.654 会变成 1.66
     * BigDecimal.ROUND_DOWN 直接删除多余的小数位：例如2.35 会变成 2.3
     * BigDecimal.ROUND_HALF_UP 四舍五入
     */
    public static String addUp(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2).setScale(scale, RoundingMode.UP).toString();
    }

    /**
     * @param v1    被减数
     * @param v2    减数
     * @param scale 保留scale 位小数
     * @param v1    被加数
     * @param v2    加数
     * @param scale 保留scale 位小数
     * @return 两个参数的和
     * @description [提供精确的加法运算，向下取整]
     * BigDecimal.ROUND_UP 进位处理：例如：1.654 会变成 1.66
     * BigDecimal.ROUND_DOWN 直接删除多余的小数位：例如2.35 会变成 2.3
     * BigDecimal.ROUND_HALF_UP 四舍五入
     */
    public static String addDown(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2).setScale(scale, RoundingMode.DOWN).toString();
    }

    /**
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     * @description [提供精确的减法运算]
     */
    public static double sub(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }

    /**
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     * @description [提供精确的减法运算]
     */
    public static BigDecimal sub(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2);
    }

    /**
     * @param v1    被减数
     * @param v2    减数
     * @param scale 保留scale 位小数
     * @return 两个参数的差
     * @description [提供精确的减法运算]
     */
    public static String sub(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2).setScale(scale, RoundingMode.HALF_UP).toString();
    }

    /**
     * @param v1    被减数
     * @param v2    减数
     * @param scale 保留scale 位小数
     * @return 两个参数的差
     * @description [提供精确的减法运算，进位处理]
     * BigDecimal.ROUND_UP 进位处理：例如：1.654 会变成 1.66
     * BigDecimal.ROUND_DOWN 直接删除多余的小数位：例如2.35 会变成 2.3
     * BigDecimal.ROUND_HALF_UP 四舍五入
     */
    public static String subUp(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2).setScale(scale, RoundingMode.UP).toString();
    }

    /**
     * @param v1    被减数
     * @param v2    减数
     * @param scale 保留scale 位小数
     * @return 两个参数的差
     * @description [提供精确的减法运算，向下取整]
     * BigDecimal.ROUND_UP 进位处理：例如：1.654 会变成 1.66
     * BigDecimal.ROUND_DOWN 直接删除多余的小数位：例如2.35 会变成 2.3
     * BigDecimal.ROUND_HALF_UP 四舍五入
     */
    public static String subDown(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2).setScale(scale, RoundingMode.DOWN).toString();
    }

    /**
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     * @description [提供精确的乘法运算]
     */
    public static double mul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal mul(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2);
    }

    /**
     * @param v1    被乘数
     * @param v2    乘数
     * @param scale 保留scale 位小数
     * @return 两个参数的积
     * @description [提供精确的乘法运算]
     */
    public static double mul(double v1, double v2, int scale) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return round(b1.multiply(b2).doubleValue(), scale);
    }

    /**
     * @param v1    被乘数
     * @param v2    乘数
     * @param scale 保留scale 位小数
     * @return 两个参数的积
     * @description [提供精确的乘法运算，四舍五入]
     * BigDecimal.ROUND_UP 进位处理：例如：1.654 会变成 1.66
     * BigDecimal.ROUND_DOWN 直接删除多余的小数位：例如2.35 会变成 2.3
     * BigDecimal.ROUND_HALF_UP 四舍五入
     */
    public static String mul(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2).setScale(scale, RoundingMode.HALF_UP).toString();
    }

    /**
     * @param v1    被乘数
     * @param v2    乘数
     * @param scale 保留scale 位小数
     * @return 两个参数的积
     * @description [提供精确的乘法运算，进位处理]
     * BigDecimal.ROUND_UP 进位处理：例如：1.654 会变成 1.66
     * BigDecimal.ROUND_DOWN 直接删除多余的小数位：例如2.35 会变成 2.3
     * BigDecimal.ROUND_HALF_UP 四舍五入
     */
    public static String mulUp(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2).setScale(scale, RoundingMode.UP).toString();
    }

    /**
     * @param v1    被乘数
     * @param v2    乘数
     * @param scale 保留scale 位小数
     * @return 两个参数的积
     * @description [提供精确的乘法运算，直接删除多余的小数位]
     * BigDecimal.ROUND_UP 进位处理：例如：1.654 会变成 1.66
     * BigDecimal.ROUND_DOWN 直接删除多余的小数位：例如2.35 会变成 2.3
     * BigDecimal.ROUND_HALF_UP 四舍五入
     */
    public static String mulDown(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2).setScale(scale, RoundingMode.DOWN).toString();
    }

    /**
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     * @description [提供（相对）精确的除法运算，当发生除不尽的情况时，精确到]
     * 小数点以后10位，以后的数字四舍五入
     */
    public static double div(double v1, double v2) {
        return div(v1, v2, DEF_DIV_SCALE);
    }

    /**
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     * @description [提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指定精度，以后的数字四舍五入]
     * 四舍五入
     * BigDecimal.ROUND_UP 进位处理：例如：1.654 会变成 1.66
     * BigDecimal.ROUND_DOWN 直接删除多余的小数位：例如2.35 会变成 2.3
     * BigDecimal.ROUND_HALF_UP 四舍五入
     */
    public static double div(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示需要精确到小数点以后几位
     * @return 两个参数的商
     * @description [提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指定精度，以后的数字四舍五入]
     * 四舍五入
     * BigDecimal.ROUND_UP 进位处理：例如：1.654 会变成 1.66
     * BigDecimal.ROUND_DOWN 直接删除多余的小数位：例如2.35 会变成 2.3
     * BigDecimal.ROUND_HALF_UP 四舍五入
     */
    public static String div(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.divide(b2, scale, RoundingMode.HALF_UP).toString();
    }

    /**
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示需要精确到小数点以后几位
     * @return 两个参数的商
     * @description [提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指定精度，以后的数字四舍五入]
     * BigDecimal.ROUND_UP 进位处理：例如：1.654 会变成 1.66
     * BigDecimal.ROUND_DOWN 直接删除多余的小数位：例如2.35 会变成 2.3
     * BigDecimal.ROUND_HALF_UP 四舍五入
     */
    public static String divUp(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.divide(b2, scale, RoundingMode.UP).toString();
    }

    /**
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示需要精确到小数点以后几位
     * @return 两个参数的商
     * @description [提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指定精度，以后的数字四舍五入]
     * BigDecimal.ROUND_UP 进位处理：例如：1.654 会变成 1.66
     * BigDecimal.ROUND_DOWN 直接删除多余的小数位：例如2.35 会变成 2.3
     * BigDecimal.ROUND_HALF_UP 四舍五入
     */
    public static String divDown(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.divide(b2, scale, RoundingMode.DOWN).toString();
    }

    /**
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     * @description [提供精确的小数位四舍五入处理]
     */
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        return b.setScale(scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     * @description [提供精确的小数位四舍五入处理]
     */
    public static String round(String v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(v);
        return b.setScale(scale, RoundingMode.HALF_UP).toString();
    }

    /**
     * @param v1    被除数
     * @param v2    除数
     * @param scale 小数点后保留几位
     * @return 余数
     * @description [取余数]
     */
    public static String remainder(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.remainder(b2).setScale(scale, RoundingMode.HALF_UP).toString();
    }

    /**
     * @param v1    被除数
     * @param v2    除数
     * @param scale 小数点后保留几位
     * @return 余数
     * @description [取余数  BigDecimal]
     */
    public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        return v1.remainder(v2).setScale(scale, RoundingMode.HALF_UP);
    }

    /**
     * @param v1 被比较数
     * @param v2 比较数
     * @return 如果v1 大于(不包括等于)v2 则 返回true 否则false
     * @description [比较大小]
     * 返回值 = -1,表示bigdemical小于bigdemical2；
     * 返回值 = 0,表示bigdemical等于bigdemical2；
     * 返回值 = 1,表示bigdemical大于bigdemical2；
     */
    public static boolean compare(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        int bj = b1.compareTo(b2);
        boolean res;
        if (bj > 0)
            res = true;
        else
            res = false;
        return res;
    }

    /**
     * @param v1 被比较数
     * @param v2 比较数
     * @return 如果v1 大于(包括等于)v2 则 返回true 否则false
     * @description [比较大小]
     * 返回值 = -1,表示bigdemical小于bigdemical2；
     * 返回值 = 0,表示bigdemical等于bigdemical2；
     * 返回值 = 1,表示bigdemical大于bigdemical2；
     */
    public static boolean compareEqual(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        int bj = b1.compareTo(b2);
        boolean res;
        if (bj >= 0)
            res = true;
        else
            res = false;
        return res;
    }

    /**
     * @param v1 被比较数
     * @param v2 比较数
     * @return 如果v1 大于v2
     * 返回值 = -1,表示bigdemical小于bigdemical2；
     * 返回值 = 0,表示bigdemical等于bigdemical2；
     * 返回值 = 1,表示bigdemical大于bigdemical2；
     * @description [比较大小]
     */
    public static int compareTo(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        int bj = b1.compareTo(b2);
        return bj;
    }

    public static void main(String[] args) {
        System.out.println("随机数：" + (int) (Math.random() * 100 + 1));
        boolean compare = BigDecimalUtil.compare("0", "0");
        int num = BigDecimalUtil.compareTo("1", "0");
        System.out.println("大小：" + compare + "-num：" + num);
        /* 加法测试 */
        String add = BigDecimalUtil.add("1", "3", 2);
        System.out.println("add：" + add);
        String addUp = BigDecimalUtil.addUp("0.333", "0.333", 2);
        System.out.println("addUp：" + addUp);
        String addDown = BigDecimalUtil.addDown("0.333", "0.333", 2);
        System.out.println("addDown：" + addDown);
        /* 减法测试 */
        String sub = BigDecimalUtil.sub("1", "3.666", 2);
        System.out.println("sub：" + sub);
        String subUp = BigDecimalUtil.subUp("0.333", "0.111", 2);
        System.out.println("subUp：" + subUp);
        String subDown = BigDecimalUtil.subDown("0.333", "0.111", 2);
        System.out.println("subDown：" + subDown);
        String mul = BigDecimalUtil.mul("1", "3", 2);
        /* 乘法测试 */
        System.out.println("mul：" + mul);
        String mulUp = BigDecimalUtil.mulUp("0.333", "2", 2);
        System.out.println("mulUp：" + mulUp);
        String mulDown = BigDecimalUtil.mulDown("0.30", "2", 2);
        System.out.println("mulDown：" + mulDown);
        String div = BigDecimalUtil.div("1", "3", 2);
        /* 除法测试 */
        System.out.println("div：" + div);
        String divUp = BigDecimalUtil.divUp("1", "3", 2);
        System.out.println("divUp：" + divUp);
        String divDown = BigDecimalUtil.divDown("1", "4", 1);
        System.out.println("divDown：" + divDown);
        System.out.println("compareEqual：" + BigDecimalUtil.compareEqual("0.55", "1"));
    }

    /*
        1、常用方法
        add(BigDecimal)
        BigDecimal对象中的值相加，返回BigDecimal对象

        subtract(BigDecimal)
        BigDecimal对象中的值相减，返回BigDecimal对象

        multiply(BigDecimal)
        BigDecimal对象中的值相乘，返回BigDecimal对象

        divide(BigDecimal)
        BigDecimal对象中的值相除，返回BigDecimal对象

        toString()
        将BigDecimal对象中的值转换成字符串

        doubleValue()
        将BigDecimal对象中的值转换成双精度数

        floatValue()
        将BigDecimal对象中的值转换成单精度数

        longValue()
        将BigDecimal对象中的值转换成长整数

        intValue()
        将BigDecimal对象中的值转换成整数

        2、BigDecimal大小比较
        java中对BigDecimal比较大小一般用的是bigdemical的compareTo方法
        int a = bigdemical.compareTo(bigdemical2)

        返回结果分析：
            a = -1,表示bigdemical小于bigdemical2；
            a = 0,表示bigdemical等于bigdemical2；
            a = 1,表示bigdemical大于bigdemical2；
            举例：a大于等于b
        new bigdemica(a).compareTo(new bigdemical(b)) >= 0
     */
}
